//
// Created by 77873 on 2020/7/16 0016.
//

#include "MineFFmpeg.h"


MineFFmpeg::MineFFmpeg(MineCallJava *callJava, const char *musicUrl, MinePlayStatus *playStatus) {
    this->mineCallJava = callJava;
    this->url = musicUrl;
    this->minePlayStatus = playStatus;
    exit = false;
    pthread_mutex_init(&init_mutex, NULL);
    pthread_mutex_init(&seekMutex, NULL);
}


void *decodeFFmpeg(void *data) {
    MineFFmpeg *mineFFmpeg = (MineFFmpeg *) data;
    mineFFmpeg->decodeFFmpegThread();
    pthread_exit(&mineFFmpeg->decodeThread);
}

void MineFFmpeg::parpared() {
    pthread_create(&decodeThread, NULL, decodeFFmpeg, this);

}


int avformatCallBack(void *ctx) {
    MineFFmpeg *fFmpeg = static_cast<MineFFmpeg *>(ctx);
    if (fFmpeg->minePlayStatus->exit) {
        return AVERROR_EOF;
    }
    return 0;
}

void MineFFmpeg::decodeFFmpegThread() {

    if (LOG_DEBUG) {
//        LOGE("decodeFFmpegThread  start");
    }
    /*加锁*/
    pthread_mutex_lock(&init_mutex);

    av_register_all();
    avformat_network_init();

    pFormatCtx = avformat_alloc_context();

    pFormatCtx->interrupt_callback.callback = avformatCallBack;
    pFormatCtx->interrupt_callback.opaque = this;

    if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0) {

        if (LOG_DEBUG) {
            LOGD("avformat_open_input error ");
            mineCallJava->onCallError(CHILD_THREAD, 1001, "avformat_open_input error ");
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {

        if (LOG_DEBUG) {
            LOGD("avformat_find_stream_info error");
            mineCallJava->onCallError(CHILD_THREAD, 1002, "avformat_find_stream_info error ");
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    /**
     * 根据pFormatCtx 遍历查找 获取对应的流
     */
    for (int i = 0; i < pFormatCtx->nb_streams; ++i) {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {

            if (audio == NULL) {

                audio = new MineAudio(minePlayStatus, mineCallJava,
                                      pFormatCtx->streams[i]->codecpar->sample_rate);
                audio->streamIndex = i;
                audio->codecParameters = pFormatCtx->streams[i]->codecpar;
                audio->duration = pFormatCtx->duration / AV_TIME_BASE;//计算总时长
                audio->time_base = pFormatCtx->streams[i]->time_base;
                duration = audio->duration;
                audio->callJava->onPcmRateCallBack(audio->sample_Reate);
            }
        }

    }

    /**
     * 根据流去查找解码器
     */
    AVCodec *dec = avcodec_find_decoder(audio->codecParameters->codec_id);

    if (!dec) {
        if (LOG_DEBUG) {
            LOGE("avcodec_find_decoder error");

            mineCallJava->onCallError(CHILD_THREAD, 1003, "avcodec_find_decoder error");
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    /**
     * 获取解码器的上下文对象
     */
    audio->avCodecContext = avcodec_alloc_context3(dec);

    if (!audio->avCodecContext) {

        if (LOG_DEBUG) {
            LOGE("avcodec_alloc_context3   error");
            mineCallJava->onCallError(CHILD_THREAD, 1004, "avcodec_alloc_context3 error");
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }
    /**
     *根据提供的编解码器中的值填充编解码器上下文参数。
     */
    if (avcodec_parameters_to_context(audio->avCodecContext,
                                      audio->codecParameters) < 0) {

        if (LOG_DEBUG) {
            LOGE("avcodec_parameters_to_context error");
            mineCallJava->onCallError(CHILD_THREAD, 1005, "avcodec_parameters_to_context error");

        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    if (avcodec_open2(audio->avCodecContext, dec, 0) != 0) {
        if (LOG_DEBUG) {
            LOGE("avcodec_open2 error");
            mineCallJava->onCallError(CHILD_THREAD, 1006, "avcodec_open2 error");

        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }


    if (NULL != mineCallJava) {
        if (LOG_DEBUG) {
            LOGE("decodeFFmpegThread  mineCallJava  is not null");
        }
        if (minePlayStatus != NULL && !minePlayStatus->exit) {
            mineCallJava->onCallParpared(CHILD_THREAD);
        } else {
            exit = true;
        }
    }
    pthread_mutex_unlock(&init_mutex);
}

void MineFFmpeg::start() {

    if (LOG_DEBUG) {
//        LOGE("MineFFmpeg  start");
    }

    if (audio == NULL) {

        if (LOG_DEBUG) {
            LOGE("audio  is NULL");
            mineCallJava->onCallError(CHILD_THREAD, 1007, "audio  is NULL ");
        }
        return;
    }

    audio->play();

    int count = 0;

    while (minePlayStatus != NULL && !minePlayStatus->exit) {
        if (minePlayStatus->seek) {
            av_usleep(1000*100);
            continue;
        }
        //添加休眠等待后，播放音频一卡一卡的， 是因为队列中没有缓冲数据，所以将其改大一点
        if(audio->mineQueue->getQueueSize()>40){
            av_usleep(1000*100);
            continue;
        }


        AVPacket *avPacket = av_packet_alloc();

        pthread_mutex_lock(&seekMutex);
        int ret = av_read_frame(pFormatCtx, avPacket);
        pthread_mutex_unlock(&seekMutex);

        if (ret == 0) {
            if (avPacket->stream_index == audio->streamIndex) {

                count++;

                if (LOG_DEBUG) {
//                    LOGE("avPacket解码 %d 帧", count);
                }
                audio->mineQueue->inAvpacket(avPacket);
            } else {
                av_packet_free(&avPacket);
                av_free(avPacket);
            }
        } else {

            if (LOG_DEBUG) {
                LOGE("decode  finished ");
            }
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;

            while (minePlayStatus != NULL && !minePlayStatus->exit) {
                if (audio->mineQueue->getQueueSize() > 0) {
                    av_usleep(1000*100);
                    continue;
                } else {
                    minePlayStatus->exit = true;
                    break;
                }
            }
        }
    }
    if(mineCallJava!=NULL){
        mineCallJava->onComplete(CHILD_THREAD);
    }
    exit = true;
    if (LOG_DEBUG) {
        LOGE("解码完成");
    }
}

void MineFFmpeg::pause() {
    if (audio != NULL) {
        audio->pause();
    }
}

void MineFFmpeg::resume() {
    if (audio != NULL) {
        audio->resume();
    }
}

void MineFFmpeg::release() {
    if (LOG_DEBUG) {
        LOGE("MineFFmpeg::release()");
    }

    //if (minePlayStatus->exit) {
    //    return;
    //}
    minePlayStatus->exit = true;

    pthread_mutex_lock(&init_mutex);

    int sleepCount = 0;
    //退出等待
    while (!exit) {
        if (sleepCount > 1000) {
            exit = true;
        }
        if (LOG_DEBUG) {
            LOGE("wait ffmpeg  exit %d", sleepCount);
        }
        sleepCount++;
        av_usleep(1000 * 100);
    }
    if (LOG_DEBUG) {
        LOGE("释放 Audio");
    }
    if (audio != NULL) {
        audio->release();
        delete (audio);
        audio = NULL;
    }
    if (LOG_DEBUG) {
        LOGE("释放 封装格式上下文");
    }
    if (pFormatCtx != NULL) {
        avformat_close_input(&pFormatCtx);
        avformat_free_context(pFormatCtx);
        pFormatCtx = NULL;
    }

    if (LOG_DEBUG) {
        LOGE("释放 callJava");
    }
    if (mineCallJava != NULL) {
        mineCallJava = NULL;
    }
    if (LOG_DEBUG) {
        LOGE("释放 playstatus");
    }
    if (minePlayStatus != NULL) {
        minePlayStatus = NULL;
    }
    pthread_mutex_unlock(&init_mutex);

}

MineFFmpeg::~MineFFmpeg() {
    pthread_mutex_destroy(&init_mutex);
    pthread_mutex_destroy(&seekMutex);
}

void MineFFmpeg::seek(int64_t second) {
    if (duration <= 0) {
        return;
    }

    if (second >= 0 && second < duration) {
        if (audio != NULL) {
            minePlayStatus->seek = true;
            audio->mineQueue->clearAvpacket();
            audio->clock = 0;
            audio->last_time=0;//解决多次点击seek时，时间不会回调问题
            pthread_mutex_lock(&seekMutex);
            int64_t rel = second * AV_TIME_BASE;

            //缓冲区内，有之前缓冲的音频帧，播放完成后，才会播放seek指定的音频
            //所以通过此方法， 清空之前的缓冲
            avcodec_flush_buffers(audio->avCodecContext);

            /**seek 关键方法*/
            avformat_seek_file(pFormatCtx,
                               -1,
                               INT64_MIN,
                               rel,
                               INT64_MAX,
                               0);

            pthread_mutex_unlock(&seekMutex);
            minePlayStatus->seek = false;
        }
    }

}

void MineFFmpeg::setVolume(int percnet) {
    if (NULL != audio) {
        audio->setVolume(percnet);
    }
}

void MineFFmpeg::setMute(int mute) {
    if(audio!=NULL){
        audio->setMute(mute);
    }
}

void MineFFmpeg::setPitch(float pitch) {
    if(NULL!=audio){
        audio->setPitch(pitch);
    }
}

void MineFFmpeg::setSpeed(float speed) {
    if(NULL!=audio){
        audio->setSpeed(speed);
    }
}

int MineFFmpeg::getSampleRate() {
    if(NULL!=audio){
        return audio->avCodecContext->sample_rate;
    }
    return 0;
}

void MineFFmpeg::startStopRecord(bool start) {
    if(NULL!=audio){
        audio->startStopRecord(start);
    }
}

bool MineFFmpeg::curAudioPlay(int startTime, int endTime, bool showPcm) {
    if(startTime>=0&&startTime<=duration
        &&endTime<=duration){
        audio->isCut= true;
        audio->end_time=endTime;
        audio->showPcm=showPcm;
        seek(startTime);
        return true;
    }
    return false;
}
